/**
 * Copyright (c) 2008, George Taylor
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are permitted 
 * provided that the following conditions are met:
 * 
 *   * Redistributions of source code must retain the above copyright notice, this list of 
 *     conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright notice, this list of 
 *     conditions and the following disclaimer in the documentation and/or other materials 
 *     provided with the distribution.
 *   * Neither the name of the copyright holder's organization nor the names of its contributors 
 *     may be used to endorse or promote products derived from this software without specific 
 *     prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package uk.co.taylorconsulting.annoweb.web.builder;

import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;

import org.apache.log4j.Logger;

import uk.co.taylorconsulting.annoweb.annotation.cache.CacheStore;
import uk.co.taylorconsulting.annoweb.annotation.form.Clean;
import uk.co.taylorconsulting.annoweb.annotation.form.validation.Param;
import uk.co.taylorconsulting.annoweb.annotation.form.validation.Required;
import uk.co.taylorconsulting.annoweb.annotation.injection.DefaultCacheValue;
import uk.co.taylorconsulting.annoweb.annotation.injection.DefaultValue;
import uk.co.taylorconsulting.annoweb.annotation.injection.Inject;
import uk.co.taylorconsulting.annoweb.web.builder.data.FormData;
import uk.co.taylorconsulting.annoweb.web.builder.data.Globals;

public class FormBuilder extends SiteBuilder {
	private static final Logger      LOG      = Logger.getLogger(SiteBuilder.class);
	private static final FormBuilder instance = new FormBuilder();
	
	public static String createHelper(Class<?> c) {
		return instance.createFormHelper(c);
	}
	
	private String createFormHelper(Class<?> c) {
		String clazzName = "Form" + convertName(c.getName());
		return clazzName;
	}
	
	public static void buildFormHelper(FormData fd, File tmpLocation, Globals globals) {
		instance.buildFormHelper(fd, tmpLocation, globals, true);
	}
	
	public void buildFormHelper(FormData fd, File tmpLocation, Globals globals, boolean ok) {
		Class<?> c = fd.getClazz();
		String clazzName = createFormHelper(c);
		File output = new File(tmpLocation, clazzName + ".java");
		if (!output.exists()) {
			StringBuffer str = new StringBuffer("import javax.servlet.http.*;\n");
			str.append("import javax.servlet.*;\n");
			str.append("import java.util.*;\n");
			str.append("public class ").append(clazzName).append(" extends  uk.co.taylorconsulting.web.controller.WebHelper {\n");
			str.append("\tprivate static final ").append(clazzName).append(" instance = new ").append(clazzName).append("();\n");
			str.append("\tprivate ").append(clazzName).append("(){}\n");
			str.append("\tpublic static ").append(c.getCanonicalName()).append(" getForm(HttpServletRequest request, String name, boolean clean, String id) throws uk.co.taylorconsulting.web.builder.ParameterRequiredException{\n");
			str.append("\t\tif (name==null || name.trim().equals(\"\")) {\n");
			str.append("\t\t\tname=\"\";\n");
			str.append("\t\t} else {\n");
			str.append("\t\t\tname= name+\".\";\n");
			str.append("\t\t}\n");
			str.append("\t\treturn instance.getForm(request, name, clean, id, true);\n");
			str.append("\t}\n");
			str.append("\tpublic ").append(c.getCanonicalName()).append(" getForm(HttpServletRequest request, String name, boolean clean, String id, boolean ok) throws uk.co.taylorconsulting.web.builder.ParameterRequiredException{\n");
			Method cleanMethod = null;
			for (Method m : c.getMethods()) {
				if (m.getAnnotation(Clean.class) != null) {
					if (cleanMethod == null) {
						cleanMethod = m;
					}
				}
			}
			str.append("\t\t").append(c.getCanonicalName()).append(" form = null;\n");
			boolean first = true;
			ArrayList<String> names = new ArrayList<String>();
			for (String key : fd.getInjects().keySet()) {
				Inject i = fd.getInjects().get(key);
				// if (i.id() != null) {
				// key = i.id();
				// }
				if (!names.contains(key)) {
					names.add(key);
					if (first) {
						str.append("\t\tif (");
						first = false;
					} else {
						str.append("\t\t} else if (");
					}
					str.append("\"").append(key).append("\".equals(id)) {\n");
					switch (i.cache().store()) {
						case REQUEST: {
							str.append("\t\t\tform = (").append(c.getCanonicalName()).append(")request.getAttribute(id);\n");
							str.append("\t\t\tif (form == null) {\n");
							str.append("\t\t\t\tform = new ").append(c.getCanonicalName()).append("();\n");
							str.append("\t\t\t\trequest.setAttribute(id,form);\n");
							str.append("\t\t\t}\n");
							break;
						}
						case SESSION: {
							str.append("\t\t\tform = (").append(c.getCanonicalName()).append(")request.getSession().getAttribute(id);\n");
							str.append("\t\t\tif (form == null) {\n");
							str.append("\t\t\t\tform = new ").append(c.getCanonicalName()).append("();\n");
							str.append("\t\t\t\trequest.getSession().setAttribute(id,form);\n");
							str.append("\t\t\t}\n");
							break;
						}
						case APPLICATION: {
							str.append("\t\t\tform = (").append(c.getCanonicalName()).append(")uk.co.taylorconsulting.web.controller.CacheManager.getItemFromCache(request, uk.co.taylorconsulting.annotation.cache.CacheStore.").append(
							        i.cache().store().toString()).append(",\"").append(i.cache().cacheId()).append("\",\"").append(i.cache().id()).append("\");\n");
							str.append("\t\t\tif (form == null) {\n");
							str.append("\t\t\t\tform = new ").append(c.getCanonicalName()).append("();\n");
							str.append("\t\t\t\tuk.co.taylorconsulting.web.controller.CacheManager.addToCache(request, uk.co.taylorconsulting.annotation.cache.CacheStore.").append(i.cache().store().toString()).append(",\"").append(
							        i.cache().cacheId()).append("\",\"").append(i.cache().id()).append("\",form);\n");
							str.append("\t\t\t}\n");
							break;
						}
						case NONE: {
							str.append("\t\t\tform = new ").append(c.getCanonicalName()).append("();\n");
							break;
						}
					}
				}
			}
			for (String key : fd.getDefaultCacheValues().keySet()) {
				DefaultCacheValue dcv = fd.getDefaultCacheValues().get(key);
				if (dcv.id() != null) {
					key = dcv.id();
				}
				if (!names.contains(key)) {
					names.add(key);
					if (first) {
						str.append("\t\tif (");
						first = false;
					} else {
						str.append("\t\t} else if (");
					}
					str.append("\"").append(key).append("\".equals(id)) {\n");
					switch (dcv.cache().store()) {
						case REQUEST: {
							str.append("\t\t\tform = (").append(c.getCanonicalName()).append(")request.getAttribute(id);\n");
							str.append("\t\t\tif (form == null) {\n");
							str.append("\t\t\t\tform = new ").append(c.getCanonicalName()).append("();\n");
							str.append("\t\t\t\trequest.setAttribute(id,form);\n");
							str.append("\t\t\t}\n");
							break;
						}
						case SESSION: {
							str.append("\t\t\tform = (").append(c.getCanonicalName()).append(")request.getSession().getAttribute(id);\n");
							str.append("\t\t\tif (form == null) {\n");
							str.append("\t\t\t\tform = new ").append(c.getCanonicalName()).append("();\n");
							str.append("\t\t\t\trequest.getSession().setAttribute(id,form);\n");
							str.append("\t\t\t}\n");
							break;
						}
						case APPLICATION: {
							str.append("\t\t\tform = (").append(c.getCanonicalName()).append(")uk.co.taylorconsulting.web.controller.CacheManager.getItemFromCache(request, uk.co.taylorconsulting.annotation.cache.CacheStore.").append(
							        dcv.cache().store().toString()).append(",\"").append(dcv.cache().cacheId()).append("\",\"").append(dcv.cache().id()).append("\");\n");
							str.append("\t\t\tif (form == null) {\n");
							str.append("\t\t\t\tform = new ").append(c.getCanonicalName()).append("();\n");
							str.append("\t\t\t\tuk.co.taylorconsulting.web.controller.CacheManager.addToCache(request, uk.co.taylorconsulting.annotation.cache.CacheStore.").append(dcv.cache().store().toString()).append(",\"").append(
							        dcv.cache().cacheId()).append("\",\"").append(dcv.cache().id()).append("\",form);\n");
							str.append("\t\t\t}\n");
							break;
						}
						case NONE: {
							str.append("\t\t\tform = new ").append(c.getCanonicalName()).append("();\n");
							break;
						}
					}
				}
			}
			if (!first) {
				str.append("\t\t} else {\n");
				str.append("\t\t\tform = new ").append(c.getCanonicalName()).append("();\n");
				str.append("\t\t}\n");
			} else {
				str.append("\t\tform = new ").append(c.getCanonicalName()).append("();\n");
			}
			for (Method m : c.getMethods()) {
				first = true;
				names.clear();
				if (m.getName().toLowerCase().startsWith("set") && m.getParameterTypes().length == 1) {
					// setter method
					Class<?> clazz = m.getParameterTypes()[0];
					MethodTypeEnum method = MethodTypeEnum.getByClass(clazz);
					if (method != null) {
						str.append("\t\t").append(clazz.getCanonicalName()).append(" tmp").append(m.getName()).append("=").append(buildMethodSigniture(m, m.getAnnotation(Required.class), true, globals)).append(";\n");
						str.append("\t\tif (").append(" tmp").append(m.getName()).append("==").append(method.getDefaultValue()).append(") {\n");
						String var = m.getName().toLowerCase().charAt(3) + m.getName().substring(4);
						for (String key : fd.getInjects().keySet()) {
							Inject i = fd.getInjects().get(key);
							// if (i.id() != null) {
							// key = i.id();
							// }
							if (!names.contains(key)) {
								names.add(key);
								for (Param p : i.defaultParameters()) {
									if (p.name().equals(var)) {
										if (first) {
											str.append("\t\t\tif (");
											first = false;
										} else {
											str.append("\t\t\t} else if (");
										}
										str.append("\"").append(key).append("\".equals(id)) {\n");
										str.append("\t\t\t\ttmp").append(m.getName()).append("=").append(method.getMethod()).append("(\"").append(p.value()).append("\");\n");
										break;
									}
								}
							}
						}
						DefaultValue dv = m.getAnnotation(DefaultValue.class);
						DefaultCacheValue dcv = m.getAnnotation(DefaultCacheValue.class);
						if (dcv != null && dcv.cache().store() != CacheStore.NONE) {
							if (!first) {
								str.append("\t\t\t} else {\n");
							}
							switch (dcv.cache().store()) {
								case REQUEST: {
									str.append("\t\t\t\ttmp").append(m.getName()).append(" = (").append(clazz.getCanonicalName()).append(")request.getAttribute(\"").append(dcv.id()).append("\");\n");
									break;
								}
								case SESSION: {
									str.append("\t\\tt\ttmp").append(m.getName()).append(" = (").append(clazz.getCanonicalName()).append(")request.getSession().getAttribute(\"").append(dcv.id()).append("\");\n");
									break;
								}
								case APPLICATION: {
									if (clazz.isPrimitive()) {
										String prim = clazz.getCanonicalName();
										prim = prim.toUpperCase().charAt(0) + prim.substring(1);
										str.append("\t\t\t\ttmp").append(m.getName()).append(" = (").append(c.getCanonicalName()).append(")uk.co.taylorconsulting.web.controller.CacheManager.get").append(prim).append(
										        "FromCache(request, uk.co.taylorconsulting.annotation.cache.CacheStore.").append(dcv.cache().store().toString()).append(",\"").append(dcv.cache().cacheId()).append("\",\"").append(
										        dcv.cache().id()).append("\");\n");
									} else {
										str.append("\t\t\t\ttmp").append(m.getName()).append(" = (").append(c.getCanonicalName()).append(
										        ")uk.co.taylorconsulting.web.controller.CacheManager.getItemFromCache(request, uk.co.taylorconsulting.annotation.cache.CacheStore.").append(dcv.cache().store().toString()).append(",\"")
										        .append(dcv.cache().cacheId()).append("\",\"").append(dcv.cache().id()).append("\");\n");
									}
									break;
								}
							}
						}
						if (dv != null) {
							if (dcv != null) {
								str.append("\t\t\t\tif (").append(" tmp").append(m.getName()).append("==").append(method.getDefaultValue()).append(") {\n");
								str.append("\t\t\t\t}\n");
							} else {
								if (!first) {
									str.append("\t\t\t} else {\n");
								}
							}
							str.append("\t\t\t\t\ttmp").append(m.getName()).append(" = (").append(clazz.getCanonicalName()).append(")").append(method.getMethod()).append("(\"").append(dv.value()).append("\");\n");
							if (dcv != null) {
								str.append("\t\t\t\t}\n");
							}
						}
						if (!first || dv != null || (dcv != null && dcv.cache().store() != CacheStore.NONE)) {
							str.append("\t\t\t}\n");
						}
						str.append("\t\t}\n");
						str.append("\t\tform.").append(m.getName()).append("(tmp").append(m.getName()).append(");\n");
					}
				}
			}
			str.append("\t\treturn form;\n");
			str.append("\t}\n");
			str.append("}\n");
			FileOutputStream fos;
			try {
				fos = new FileOutputStream(output);
				fos.write(str.toString().getBytes());
				fos.close();
			} catch (Exception e) {
				LOG.warn("Error writing class to " + output.getAbsolutePath() + "(" + e.getMessage() + ")");
			}
		}
	}
	
	private String convertName(String name) {
		return name.replace('.', '_');
	}
}
